import struct
import numpy as np
import logging
import os
import mmap
import gc
import time
from typing import Generator, Optional, Callable, Tuple
from PyQt5.QtWidgets import QApplication
from logger import logger

# 新增内存监控和优化
try:
    import psutil
    HAS_PSUTIL = True
except ImportError:
    HAS_PSUTIL = False
    logging.warning("psutil不可用，内存监控功能受限")

# 导入优化的文件处理器
try:
    from optimized_file_processor import OptimizedFileProcessor
    HAS_OPTIMIZED_PROCESSOR = True
except ImportError:
    HAS_OPTIMIZED_PROCESSOR = False
    logging.warning("优化文件处理器不可用")


class MultiFormatFrameProcessor:
    """支持多种帧格式的处理器
    
    支持的格式：
    - 1092字节：原始帧格式
    - 1024字节：纯数据帧格式（4字节同步头 + 1020字节数据）
    - 896字节：仅数据区格式
    """
    
    # 定义不同格式的帧结构
    FMT_1092 = np.dtype([
        ('sync_word', '>u4'),
        ('length', '>u4'),
        ('zero', '>u4'),
        ('t1', '>u4'),
        ('t2', '>u4'),
        ('frame_count', '>u4'),
        ('帧检验结果', '>u4'),
        ('帧同步状态', '>u4'),
        ('位滑状态', '>u4'),
        ('virtual_channel', '>u4'),
        ('frame_len', '>u4'),
        ('同步字长度', '>u4'),
        ('decode_status', '>u4'),
        ('同步字状态', '>u4'),
        ('f_001', '>u4', 2),
        ('f896', [
            ('sync', '>u4'),
            ('h1', '>u2'),
            ('h2', '>u4'),
            ('payload', 'S886')
        ]),
        ('f128', 'V128'),
        ('synn_end', '>u4'),
    ])
    
    FMT_1024 = np.dtype([
        ('sync_word', '>u4'),        # 同步头 0x1ACFFC1D
        ('version_scid', '>u1'),     # 版本号(高2位)+航天器标识(低6位)
        ('vcid', '>u1'),             # 虚拟信道标识(低6位)
        ('vc_count', 'V3'),          # 虚拟信道帧计数（3字节）
        ('flag', '>u1'),             # 标志域
        ('insert_zone', 'V8'),       # 插入域
        ('data_zone', 'V878'),       # 数据域
        ('rs_check', 'V128')         # RS校验码
    ])
    
    # 896字节格式就是直接从1092字节帧中提取的数据区
    FMT_896 = np.dtype([
        ('data', 'V896')
    ])
    
    def __init__(self):
        self.logger = logging.getLogger('frame_analyzer')
        self.optimized_processor = OptimizedFileProcessor() if HAS_OPTIMIZED_PROCESSOR else None
        
    def check_memory_usage(self):
        """检查内存使用情况"""
        if HAS_PSUTIL:
            try:
                memory = psutil.virtual_memory()
                if memory.percent > 85:
                    self.logger.warning(f"内存使用率高: {memory.percent:.1f}%")
                    gc.collect()
                    return False
                return True
            except:
                return True
        return True
        
    def get_available_memory(self):
        """获取可用内存（MB）"""
        if HAS_PSUTIL:
            try:
                memory = psutil.virtual_memory()
                return memory.available / (1024 * 1024)  # 转换为MB
            except:
                return 1000  # 默认假设1GB可用
        return 1000
        
    def _should_use_optimized_processor(self, file_path: str) -> bool:
        """判断是否应该使用优化处理器
        
        根据文件大小和系统内存情况决定
        """
        if not HAS_OPTIMIZED_PROCESSOR or not self.optimized_processor:
            return False
            
        # 获取文件大小
        try:
            file_size = os.path.getsize(file_path)
            # 大于500MB的文件使用优化处理器
            if file_size > 500 * 1024 * 1024:
                self.logger.info(f"文件大小 {file_size/(1024**3):.2f}GB，使用优化处理器")
                return True
                
            # 检查内存使用情况
            if HAS_PSUTIL:
                memory = psutil.virtual_memory()
                if memory.percent > 70:  # 内存使用率超过70%
                    self.logger.info(f"内存使用率 {memory.percent:.1f}%，使用优化处理器")
                    return True
                    
        except Exception as e:
            self.logger.warning(f"检查是否使用优化处理器时出错: {e}")
            
        return False
        
    def read_frames_with_format(self, file_path: str, frame_format: str, 
                               progress_callback: Optional[Callable[[int, str, str], None]] = None) -> list:
        """根据指定格式读取帧数据
        
        Args:
            file_path: 文件路径
            frame_format: 帧格式 ('1092', '1024', '896')
            progress_callback: 进度回调函数
            
        Returns:
            list: 统一格式的帧数据列表
        """
        try:
            self.logger.info(f"使用{frame_format}字节格式读取文件: {file_path}")
            
            # 检查是否应该使用优化处理器
            if self._should_use_optimized_processor(file_path):
                try:
                    self.logger.info("使用优化处理器进行流式读取")
                    return self.optimized_processor.read_file_batch(
                        file_path, frame_format, progress_callback
                    )
                except Exception as e:
                    self.logger.warning(f"优化处理器失败，回退到标准处理: {e}")
                    # 如果优化处理器失败，继续使用标准方法
            
            # 检查内存状态
            if not self.check_memory_usage():
                self.logger.warning("内存使用率过高，建议关闭其他程序")
            
            if frame_format == '1092':
                return self._read_1092_format(file_path, progress_callback)
            elif frame_format == '1024':
                return self._read_1024_format(file_path, progress_callback)
            elif frame_format == '896':
                return self._read_896_format(file_path, progress_callback)
            else:
                raise ValueError(f"不支持的帧格式: {frame_format}")
                
        except MemoryError as e:
            self.logger.error(f"内存不足: {str(e)}")
            gc.collect()
            # 尝试使用优化处理器进行紧急处理
            if HAS_OPTIMIZED_PROCESSOR and self.optimized_processor:
                try:
                    self.logger.info("内存不足，尝试使用优化处理器进行流式读取")
                    return self.optimized_processor.read_file_batch(
                        file_path, frame_format, progress_callback
                    )
                except:
                    pass
            raise Exception("内存不足，请关闭其他程序后重试")
        except Exception as e:
            self.logger.error(f"读取文件失败: {str(e)}")
            raise
            
    def _read_1092_format(self, file_path: str, progress_callback: Optional[Callable[[int, str, str], None]]) -> list:
        """读取1092字节格式的帧数据"""
        try:
            file_size = os.path.getsize(file_path)
            if file_size % 1092 != 0:
                raise ValueError(f"文件大小({file_size}字节)不是1092的整数倍")
                
            if progress_callback:
                progress_callback(10, f"读取1092字节格式文件", f"文件大小: {file_size / (1024 * 1024):.2f} MB")
                
            # 如果是大文件，使用内存映射（降低阈值到300MB以提高稳定性）
            if file_size > 300 * 1024 * 1024:
                return self._read_1092_large_file(file_path, progress_callback)
                
            # 使用NumPy读取
            data = np.fromfile(file_path, dtype=self.FMT_1092)
            
            if progress_callback:
                progress_callback(50, f"已读取{len(data)}帧数据", "正在处理...")
                
            # 读取原始帧数据用于误码率分析
            with open(file_path, 'rb') as f:
                raw_frames_data = f.read()
                
            frames = []
            # 设置虚拟信道值
            data['virtual_channel'] = data['f896']['h1'] & 0x3F
            
            for i, frame in enumerate(data):
                if progress_callback and i % max(1, len(data) // 20) == 0:
                    progress = int(50 + (i / len(data) * 40))
                    progress_callback(progress, f"正在处理第 {i+1}/{len(data)} 帧", "")
                    
                # 构建统一的payload（包含f896和f128）
                # 使用struct.pack保持正确的字节序
                payload = (
                    struct.pack('>I', int(frame['f896']['sync'])) +      # 第64-67字节：帧同步头
                    struct.pack('>H', int(frame['f896']['h1'])) +        # 第68-69字节：版本号+航天器标识  
                    struct.pack('>I', int(frame['f896']['h2'])) +        # 第70-73字节：包含虚拟信道帧计数
                    frame['f896']['payload'] +                           # 第74-959字节：数据
                    frame['f128']                                         # 第960-1087字节：RS校验
                )
                
                # 获取当前帧的原始数据
                raw_frame_start = i * 1092
                raw_frame_end = (i + 1) * 1092
                raw_frame_data = raw_frames_data[raw_frame_start:raw_frame_end]
                
                frames.append({
                    'sync_word': int(frame['sync_word']),
                    'frame_count': int(frame['frame_count']),
                    'virtual_channel': int(frame['virtual_channel']),
                    'decode_status': int(frame['decode_status']),
                    'payload': payload,
                    'raw_frame': raw_frame_data,  # 保存原始帧数据
                    'frame_format': '1092',
                    'frame_len': 1092,
                    'frame_sync_status': int(frame['帧同步状态']),
                    'bit_slip_status': int(frame['位滑状态']),
                    '帧检验结果': int(frame['帧检验结果']),
                    '同步字状态': int(frame['同步字状态'])
                })
                
            if progress_callback:
                progress_callback(95, f"处理完成", f"共读取 {len(frames)} 帧")
                
            return frames
            
        except Exception as e:
            self.logger.error(f"读取1092格式文件失败: {str(e)}")
            raise
            
    def _read_1024_format(self, file_path: str, progress_callback: Optional[Callable[[int, str, str], None]]) -> list:
        """读取1024字节格式的帧数据"""
        try:
            file_size = os.path.getsize(file_path)
            if file_size % 1024 != 0:
                raise ValueError(f"文件大小({file_size}字节)不是1024的整数倍")
                
            if progress_callback:
                progress_callback(10, f"读取1024字节格式文件", f"文件大小: {file_size / (1024 * 1024):.2f} MB")
                
            # 如果是大文件，使用内存映射（降低阈值到300MB以提高稳定性）
            if file_size > 300 * 1024 * 1024:
                return self._read_1024_large_file(file_path, progress_callback)
                
            frames = []
            total_frames = file_size // 1024
            
            # 直接按字节读取，不使用结构化数组
            with open(file_path, 'rb') as f:
                for i in range(total_frames):
                    if progress_callback and i % max(1, total_frames // 20) == 0:
                        progress = int(50 + (i / total_frames * 40))
                        progress_callback(progress, f"正在处理第 {i+1}/{total_frames} 帧", "")
                        
                    # 读取1024字节的原始数据
                    frame_data = f.read(1024)
                    if len(frame_data) != 1024:
                        break
                        
                    # 从原始数据中提取字段
                    # 帧同步头(0-3): 大端序uint32
                    sync_word = struct.unpack('>I', frame_data[0:4])[0]
                    
                    # 虚拟信道标识(5): 低6位
                    vcid = frame_data[5] & 0x3F
                    
                    # 虚拟信道帧计数(6-8): 3字节，大端序
                    vc_count = int.from_bytes(frame_data[6:9], byteorder='big')  # 直接读取3字节
                    
                    # 检查帧同步头是否正确
                    # if sync_word != 0x1ACFFC1D:
                    #     self.logger.warning(f"帧 {i}: 帧同步头异常: 0x{sync_word:08X}")
                    
                    frames.append({
                        'sync_word': sync_word,
                        'frame_count': vc_count,  # 使用虚拟信道帧计数
                        'virtual_channel': vcid,
                        'decode_status': 0,  # 1024格式没有译码状态
                        'payload': frame_data,  # 直接使用原始1024字节数据
                        'raw_frame': frame_data,  # 保存原始帧数据
                        'frame_format': '1024',
                        'frame_len': 1024,
                        'frame_sync_status': 1,  # 假设同步正常
                        'bit_slip_status': 0,    # 假设无位滑
                        '帧检验结果': 0,
                        '同步字状态': 0
                    })
                
            if progress_callback:
                progress_callback(95, f"处理完成", f"共读取 {len(frames)} 帧")
                
            return frames
            
        except Exception as e:
            self.logger.error(f"读取1024格式文件失败: {str(e)}")
            raise
            
    def _read_896_format(self, file_path: str, progress_callback: Optional[Callable[[int, str, str], None]]) -> list:
        """读取896字节格式的帧数据"""
        try:
            file_size = os.path.getsize(file_path)
            if file_size % 896 != 0:
                raise ValueError(f"文件大小({file_size}字节)不是896的整数倍")
                
            if progress_callback:
                progress_callback(10, f"读取896字节格式文件", f"文件大小: {file_size / (1024 * 1024):.2f} MB")
                
            # 如果是大文件，使用内存映射（降低阈值到300MB以提高稳定性）
            if file_size > 300 * 1024 * 1024:
                return self._read_896_large_file(file_path, progress_callback)
                
            # 使用NumPy读取
            data = np.fromfile(file_path, dtype=self.FMT_896)
            
            if progress_callback:
                progress_callback(50, f"已读取{len(data)}帧数据", "正在处理...")
                
            frames = []
            for i, frame in enumerate(data):
                if progress_callback and i % max(1, len(data) // 20) == 0:
                    progress = int(50 + (i / len(data) * 40))
                    progress_callback(progress, f"正在处理第 {i+1}/{len(data)} 帧", "")
                    
                # 从数据中尝试解析基本信息
                # 使用.tobytes()来获取字节数据
                frame_data = frame['data'].tobytes() if hasattr(frame['data'], 'tobytes') else bytes(frame['data'])
                
                # 尝试从前4个字节获取同步字
                sync_word = int.from_bytes(frame_data[:4], byteorder='big')
                
                # 尝试从数据中解析虚拟信道和帧计数（如果数据格式允许）
                # 这里假设896字节数据的前部分包含必要的头信息
                vcid = 0
                frame_count = i  # 使用索引作为帧计数
                
                if len(frame_data) >= 9:
                    # 尝试从第5-6字节获取虚拟信道信息
                    vcid = int(frame_data[5]) & 0x3F
                    # 尝试从第6-9字节获取帧计数
                    if len(frame_data) >= 9:
                        frame_count = int.from_bytes(frame_data[6:9], byteorder='big')
                
                frames.append({
                    'sync_word': sync_word,
                    'frame_count': frame_count,
                    'virtual_channel': vcid,
                    'decode_status': 0,  # 896格式没有译码状态
                    'payload': frame_data,
                    'raw_frame': frame_data,  # 保存原始帧数据
                    'frame_format': '896',
                    'frame_len': 896,
                    'frame_sync_status': 1,
                    'bit_slip_status': 0,
                    '帧检验结果': 0,
                    '同步字状态': 0
                })
                
            if progress_callback:
                progress_callback(95, f"处理完成", f"共读取 {len(frames)} 帧")
                
            return frames
            
        except Exception as e:
            self.logger.error(f"读取896格式文件失败: {str(e)}")
            raise
            
    def _read_1092_large_file(self, file_path: str, progress_callback) -> list:
        """使用内存映射读取大的1092格式文件"""
        frames = []
        file_size = os.path.getsize(file_path)
        total_frames = file_size // 1092
        
        # 根据可用内存动态调整批量大小
        available_memory_mb = self.get_available_memory()
        if available_memory_mb < 500:
            batch_size = 100  # 内存少时使用小批量
        elif available_memory_mb < 1000:
            batch_size = 500
        else:
            batch_size = 1000
        
        self.logger.info(f"可用内存: {available_memory_mb:.0f}MB, 批量大小: {batch_size}")
        
        with open(file_path, 'rb') as f:
            with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as mm:
                for batch_idx in range(0, total_frames, batch_size):
                    batch_end = min(batch_idx + batch_size, total_frames)
                    
                    # 检查内存使用
                    if not self.check_memory_usage():
                        gc.collect()
                        time.sleep(0.1)  # 给系统时间回收内存
                    
                    if progress_callback:
                        progress = int(20 + (batch_idx / total_frames) * 70)
                        progress_callback(progress, f"处理批次 {batch_idx // batch_size + 1}", 
                                        f"帧 {batch_idx}-{batch_end}")
                    
                    # 读取批次数据
                    start_offset = batch_idx * 1092
                    end_offset = batch_end * 1092
                    batch_data = mm[start_offset:end_offset]
                    
                    # 使用NumPy解析批次 - 创建可写副本
                    batch_array = np.frombuffer(batch_data, dtype=self.FMT_1092).copy()
                    batch_array['virtual_channel'] = batch_array['f896']['h1'] & 0x3F
                    
                    for idx, frame in enumerate(batch_array):
                        # 使用struct.pack保持正确的字节序
                        payload = (
                            struct.pack('>I', int(frame['f896']['sync'])) +      # 第64-67字节：帧同步头
                            struct.pack('>H', int(frame['f896']['h1'])) +        # 第68-69字节：版本号+航天器标识  
                            struct.pack('>I', int(frame['f896']['h2'])) +        # 第70-73字节：包含虚拟信道帧计数
                            frame['f896']['payload'] +                           # 第74-959字节：数据
                            frame['f128']                                         # 第960-1087字节：RS校验
                        )
                        
                        # 计算当前帧在批次中的索引
                        frame_idx_in_batch = frames.__len__() - batch_idx
                        # 获取当前帧的原始数据
                        frame_offset_in_batch = idx * 1092
                        raw_frame_data = batch_data[frame_offset_in_batch:frame_offset_in_batch + 1092]
                        
                        frames.append({
                            'sync_word': int(frame['sync_word']),
                            'frame_count': int(frame['frame_count']),
                            'virtual_channel': int(frame['virtual_channel']),
                            'decode_status': int(frame['decode_status']),
                            'payload': payload,
                            'raw_frame': raw_frame_data,  # 保存原始帧数据
                            'frame_format': '1092',
                            'frame_len': 1092,
                            'frame_sync_status': int(frame['帧同步状态']),
                            'bit_slip_status': int(frame['位滑状态']),
                            '帧检验结果': int(frame['帧检验结果']),
                            '同步字状态': int(frame['同步字状态'])
                        })
                    
                    gc.collect()
                    QApplication.processEvents()
                    
        return frames
        
    def _read_1024_large_file(self, file_path: str, progress_callback) -> list:
        """使用内存映射读取大的1024格式文件"""
        frames = []
        file_size = os.path.getsize(file_path)
        total_frames = file_size // 1024
        batch_size = 1000
        
        with open(file_path, 'rb') as f:
            with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as mm:
                for batch_idx in range(0, total_frames, batch_size):
                    batch_end = min(batch_idx + batch_size, total_frames)
                    
                    if progress_callback:
                        progress = int(20 + (batch_idx / total_frames) * 70)
                        progress_callback(progress, f"处理批次 {batch_idx // batch_size + 1}", 
                                        f"帧 {batch_idx}-{batch_end}")
                    
                    # 读取批次数据
                    start_offset = batch_idx * 1024
                    end_offset = batch_end * 1024
                    batch_data = mm[start_offset:end_offset]
                    
                    # 使用NumPy解析批次 - 创建可写副本
                    batch_array = np.frombuffer(batch_data, dtype=self.FMT_1024).copy()
                    
                    for idx, frame in enumerate(batch_array):
                        vcid = int(frame['vcid']) & 0x3F
                        vc_count_bytes = frame['vc_count'].tobytes() if hasattr(frame['vc_count'], 'tobytes') else bytes(frame['vc_count'])
                        vc_count = int.from_bytes(vc_count_bytes, byteorder='big')
                        
                        # 获取当前帧的原始数据
                        frame_offset_in_batch = idx * 1024
                        raw_frame_data = batch_data[frame_offset_in_batch:frame_offset_in_batch + 1024]
                        
                        # 直接使用原始数据作为payload，避免tobytes()导致的字节序问题
                        payload = raw_frame_data
                        
                        frames.append({
                            'sync_word': int(frame['sync_word']),
                            'frame_count': vc_count,
                            'virtual_channel': vcid,
                            'decode_status': 0,
                            'payload': payload,
                            'raw_frame': raw_frame_data,  # 保存原始帧数据
                            'frame_format': '1024',
                            'frame_len': 1024,
                            'frame_sync_status': 1,
                            'bit_slip_status': 0,
                            '帧检验结果': 0,
                            '同步字状态': 0
                        })
                    
                    gc.collect()
                    QApplication.processEvents()
                    
        return frames
        
    def _read_896_large_file(self, file_path: str, progress_callback) -> list:
        """使用内存映射读取大的896格式文件"""
        frames = []
        file_size = os.path.getsize(file_path)
        total_frames = file_size // 896
        batch_size = 1000
        
        with open(file_path, 'rb') as f:
            with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as mm:
                for batch_idx in range(0, total_frames, batch_size):
                    batch_end = min(batch_idx + batch_size, total_frames)
                    
                    if progress_callback:
                        progress = int(20 + (batch_idx / total_frames) * 70)
                        progress_callback(progress, f"处理批次 {batch_idx // batch_size + 1}", 
                                        f"帧 {batch_idx}-{batch_end}")
                    
                    # 读取批次数据
                    start_offset = batch_idx * 896
                    end_offset = batch_end * 896
                    batch_data = mm[start_offset:end_offset]
                    
                    # 使用NumPy解析批次 - 创建可写副本
                    batch_array = np.frombuffer(batch_data, dtype=self.FMT_896).copy()
                    
                    for i, frame in enumerate(batch_array):
                        frame_data = frame['data'].tobytes() if hasattr(frame['data'], 'tobytes') else bytes(frame['data'])
                        
                        # 获取当前帧的原始数据（和frame_data相同）
                        frame_offset_in_batch = i * 896
                        raw_frame_data = batch_data[frame_offset_in_batch:frame_offset_in_batch + 896]
                        
                        sync_word = int.from_bytes(frame_data[:4], byteorder='big')
                        
                        vcid = 0
                        frame_count = batch_idx + i
                        
                        if len(frame_data) >= 9:
                            vcid = int(frame_data[5]) & 0x3F
                            frame_count = int.from_bytes(frame_data[6:9], byteorder='big')
                        
                        frames.append({
                            'sync_word': sync_word,
                            'frame_count': frame_count,
                            'virtual_channel': vcid,
                            'decode_status': 0,
                            'payload': frame_data,
                            'raw_frame': raw_frame_data,  # 保存原始帧数据
                            'frame_format': '896',
                            'frame_len': 896,
                            'frame_sync_status': 1,
                            'bit_slip_status': 0,
                            '帧检验结果': 0,
                            '同步字状态': 0
                        })
                    
                    gc.collect()
                    QApplication.processEvents()
                    
        return frames
        
    def extract_comparison_data(self, frame: dict, compare_range: tuple = None) -> bytes:
        """从帧中提取用于比对的数据
        
        Args:
            frame: 帧数据
            compare_range: 比对范围(start_byte, end_byte)，None表示比对全部
            
        Returns:
            bytes: 用于比对的数据
        """
        try:
            payload = frame['payload']
            frame_format = frame.get('frame_format', '1092')
            
            # 获取payload的实际长度
            payload_len = len(payload)
            
            # 根据帧格式确定可比对的范围
            if frame_format == '1092':
                # 1092格式：payload包含完整的1024字节数据
                max_len = min(payload_len, 1024)
                if compare_range:
                    start, end = compare_range
                    # 确保不超出实际数据范围
                    start = max(0, min(start, max_len - 1))
                    end = min(end, max_len - 1)
                    return payload[start:end+1]
                else:
                    return payload[:max_len]
                    
            elif frame_format == '1024':
                # 1024格式：payload就是完整数据
                if compare_range:
                    start, end = compare_range
                    # 确保不超出实际数据范围
                    start = max(0, min(start, payload_len - 1))
                    end = min(end, payload_len - 1)
                    return payload[start:end+1]
                else:
                    return payload
                    
            elif frame_format == '896':
                # 896格式：payload就是896字节数据
                max_len = min(payload_len, 896)
                if compare_range:
                    start, end = compare_range
                    # 确保不超出896字节范围
                    start = max(0, min(start, max_len - 1))
                    end = min(end, max_len - 1)
                    return payload[start:end+1]
                else:
                    return payload[:max_len]
                    
            else:
                # 未知格式，返回原始payload（但限制最大长度）
                if compare_range:
                    start, end = compare_range
                    start = max(0, min(start, payload_len - 1))
                    end = min(end, payload_len - 1)
                    return payload[start:end+1]
                else:
                    return payload
                    
        except Exception as e:
            self.logger.error(f"提取比对数据失败: {str(e)}, 格式: {frame_format}")
            # 返回空字节以避免崩溃
            return b'' 